#include "domain.h"
#include "../hed/face.h"
#include "../hed/loop.h"
#include "../hed/halfedge.h"
#include "../hed/edge.h"
#include <math.h>
#include <string>
#include <sstream>


extern "C"
{
#include "../mesh/triangle.h"
}



Domain::Domain( Face* _face )
: m_face( _face )
, m_attribute( NULL )
{
  Loop* loop = m_face->getFloops();
  Loop* loopOut = m_face->getLoopOut();
  do
  {
    if( loop == loopOut ) // testing first just to the external loop
    {
      HalfEdge* h = loop->getLedg();
      do
      {
        bool isInverted = true;
        if( h->getEdg()->getFirstVertex() == h->getVtx() )
          isInverted = false;
        Boundary* bound = new Boundary( h->getEdg(), isInverted );
        m_bounds.push_back( bound );
      }
      while( (h = h->getNxt()) != loop->getLedg() );
    }
  }
  while(( loop = loop->getNextl()) != NULL );

}

void 
Domain::getBoundaries( list<Boundary*>& bound )
{
  bound = m_bounds;
}


bool 
Domain::isSelected()
{
  return m_face->isSelected();
}


void 
Domain::cleanMesh()
{
  m_pts.clear();
  m_conn.clear();
}




void 
Domain::generateMesh()
{
  m_pts.clear();
  m_conn.clear();

  // obtains the boundary points of each domain
  vector<Point> bound;
  list<Boundary*>::iterator itB;
  for( itB = m_bounds.begin(); itB != m_bounds.end(); itB++ )
  {
    vector<Point> bPt = (*itB)->getSubdivisionPoints();
    for( unsigned int i=1; i<bPt.size(); i++)
    {
      bound.push_back( bPt[i] );
    }
  }

  // get boundary
  int np = (int) bound.size();
  struct triangulateio in, out, vorout;

  // points and segments
  in.numberofpoints = np;
  in.pointlist = new double[in.numberofpoints * 2];
  in.numberofsegments = np;
  in.segmentlist = new int[in.numberofpoints * 2];
  in.segmentmarkerlist = NULL;
  double maxL = sqrt( bound[1].distance2(bound[0]) );
  double minL = maxL;
  for( int i = 0; i < np; ++i )
  {
    in.pointlist[i*2]   = bound[i].getX();
    in.pointlist[i*2+1] = bound[i].getY();
    in.segmentlist[i*2+0] = i;
    in.segmentlist[i*2+1] = i+1;
    double dist2p = sqrt( bound[i].distance2(bound[(i+1)%np]) );
    if( minL > dist2p )
      minL = dist2p;
    if( maxL < dist2p )
      maxL = dist2p;
  }
  in.segmentlist[np*2-1] = 0;
  in.numberofpointattributes = 0;
  in.pointattributelist      = (REAL *)NULL;
  in.pointmarkerlist         = (int *)NULL;
  in.numberofholes           = 0;
  in.numberofregions         = 0;
  in.regionlist              = (REAL *)NULL;

  out.pointlist = (REAL *) NULL;            /* Not needed if -N switch used. */
  out.pointattributelist = (REAL *) NULL;
  out.pointmarkerlist = (int *) NULL; /* Not needed if -N or -B switch used. */
  out.trianglelist = (int *) NULL;          /* Not needed if -E switch used. */
  out.triangleattributelist = (REAL *) NULL;
  out.neighborlist = (int *) NULL;         /* Needed only if -n switch used. */
  out.segmentlist = (int *) NULL;
  out.segmentmarkerlist = (int *) NULL;
  out.edgelist = (int *) NULL;             /* Needed only if -e switch used. */
  out.edgemarkerlist = (int *) NULL;   /* Needed if -e used and -B not used. */

  double size = (maxL+minL)/2.0;
  double maxArea = maxL*maxL*sqrt(3.0)/4.0; // equilateral triangle area
  std::ostringstream sstream;
  sstream << maxArea;
  std::string varAsString1 = sstream.str();
  std::string varAsString2 = "pzq30a"+varAsString1+"YiCV";

  // Delaunay Triangulation
  //triangulate();
  triangulate((char *)varAsString2.c_str(), &in, &out, &vorout);

  // checks 
  // get number of points and triangles
  if( out.numberofpoints == 0 || out.numberoftriangles == 0 )
    return;

  // get points
  for( int i = 0; i < out.numberofpoints; ++i )
  {
    Point tmp( out.pointlist[i*2+0], out.pointlist[i*2+1], 0.0 );
    m_pts.push_back( tmp );
  }

  // get connectivity
  for( int i = 0; i < out.numberoftriangles; ++i )
  {
    vector<int> triang;
    triang.push_back( out.trianglelist[i*3+0] );
    triang.push_back( out.trianglelist[i*3+1] );
    triang.push_back( out.trianglelist[i*3+2] );
    m_conn.push_back( triang );
  }

}


void 
Domain::getCenter( double& xc, double& yc )
{
  double z;
  m_face->getLoopOut()->getCenter(xc,yc,z);
}
